Skip to content

feat: add quickpay daily spend limit - #1159

Merged
ovitrif merged 83 commits into
masterfrom
fix/require-payment-pin-for-quickpay
Aug 27, 2026
Merged

feat: add quickpay daily spend limit#1159
ovitrif merged 83 commits into
masterfrom
fix/require-payment-pin-for-quickpay

Conversation

@ovitrif

@ovitrif ovitrif commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Closes #1168

iOS port:

QuickPay stays PIN-free under limits (the point of QuickPay). A hard payment-PIN skip would remove that behavior, so this PR bounds auto-pay with a daily spend multiplier instead. Payments above the daily cap open Confirm, where PIN for payments still applies. Lock-time deferral remains in #1158.

Description

  • Keep QuickPay confirmation-free and PIN-free while under the per-tx threshold and daily cap
  • Add a daily limit multiplier on QuickPay settings (1, 3, 5, 10, 50; default 5)
  • Daily cap is threshold × multiplier in USD cents; spend rolls forward on the local calendar day
  • Bind spend to the invoice payment hash before send; settle only through signalCompletion; keep spend on success; release matching-day failure only
  • Over the daily cap, or if reserve fails at pay time → Confirm (replaces QuickPay; payment PIN only if that setting is on)
  • QuickPay success shows the amount with routing fee; Confirm keeps the invoice amount
  • Unlock/lock scan deferral from fix: secure shop payments and locked links #1158
  • Slider: labels included in layout bounds; tap settles from the current value (no jump back to the first on-screen value)
  • Internal architecture: QuickPayRepo owns sessions, in-flight ops, dispatch, and the daily-cap ledger. QuickPaySpendStore is an internal class in the same file.

Out of scope

  • RTL slider thumb offset: same on master, not a new regression
  • Clock or timezone jumps: rollback keeps spend; only a later local date opens a new bucket
  • In-flight spend after process death past midnight: at most one under-count on the new day, no over-count
  • Compose test for the QuickPay fallback snapshot: covered in QuickPayViewModelTest

Preview

Updated screenshot, integrating designer's feedback.

Screenshot
QuickPay Settings Over Cap → Confirm + PIN
slider.mp4
1159-over-daily-cap-confirm-pin.mp4

QA Notes

Manual Tests

  • 1. Settings → QuickPay: both sliders visible; tap a different stop snaps there without jumping to the first step; page scrolls when dragging vertically from a thumb; resolved daily $ copy updates
  • 2. Unlocked, under per-tx and daily cap, PIN for payments on → scan / paste a small LN invoice: QuickPay then success (no Confirm / payment PIN); success amount includes routing fee
  • 3. After the daily cap is filled → scan another under-threshold invoice: Confirm; swipe can ask for PIN
  • 4. Pay-time reserve miss / overlapping pay: Confirm replaces QuickPay; back does not return to an idle QuickPay loader
  • 5. Hold-invoice QuickPay that later fails: failure UI; a following under-cap invoice still QuickPays
  • 6. Hold-invoice QuickPay that later succeeds: success amount includes fee; spend counts toward the cap
  • 7. Confirm Lightning send (over threshold or QuickPay off) that settles immediately: success amount is the invoice amount
  • 8. Confirm Lightning send through Pending, then success: success amount is still the invoice amount
  • 9. regression: Cold start / locked with PIN → scan stays deferred until unlock (fix: secure shop payments and locked links #1158), then follows the rules above
  • 10. Pay an invoice with QuickPay → scan / paste the same invoice again: Payment Failed says the invoice has already been paid; no second payment or daily-cap charge

Automated Checks

  • Unit tests added in QuickPayRepoTest.kt:
    • daily-cap reserveBound
    • matching-day release
    • conversion failure
    • signalCompletion success keeps spend
    • signalCompletion failure rolls back
    • signalCompletion is idempotent
    • dual-alias settle
    • inlined iOS/Android ledger decode
    • dispatch-error classification
    • duplicate payment with pending LDK does not refund
    • recovered hash after reload
    • concurrent same-hash pay does not fall back to Confirm
    • live dispatched ops stay out of reconcile
    • re-pay of a settled hash does not double-count
    • reservation persists before dispatch
    • detach before dispatch aborts and releases
    • pre-dispatch rejection refunds
    • null payment rows mutate nothing
    • rescan of a live Pending invoice replays Pending
    • live records survive day prune
    • lookup throw leaves spend unchanged
    • zombie rescan after detach timeout replays Pending
    • detachAll does not sweep a session attached after snapshot
  • Unit tests added in QuickPayViewModelTest.kt:
    • session success mapping
    • session pending mapping
    • session error mapping
    • pay forwards to the repo
    • stale-session detach
    • pay re-entry is guarded until attach resets
  • Unit tests modified in AppViewModelSendFlowTest.kt:
    • under-cap QuickPay vs over-cap Confirm
    • pending settlement through signalCompletion
    • open hash routes to QuickPay when cap would skip
    • null-hash PaymentFailed still resolves Pending
    • contact flow wins over an open hash
    • open-hash routing intentionally bypasses the QuickPay-enabled setting
  • Unit tests modified in PendingPaymentRepoTest.kt:
    • late collector does not receive a buffered resolution
  • Unit tests modified in SendPendingViewModelTest.kt
  • Instrumented tests modified: daily-limit slider tags in QuickPaySettingsScreenTest.kt
  • CI: standard compile, unit test, and detekt checks run by the PR bot.

@greptile-apps

greptile-apps Bot commented Aug 15, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds a persisted daily spending cap to QuickPay while retaining confirmation-free, PIN-free payments below the configured limits.

  • Adds configurable daily-limit settings and updated QuickPay UI.
  • Introduces a payment-hash-bound ledger with reservation, settlement, rollback, and LDK reconciliation.
  • Routes over-cap payments to Confirm and carries QuickPay settlement results through pending and success states.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains in the eligible follow-up review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
app/src/main/java/to/bitkit/repositories/QuickPayRepo.kt Centralizes QuickPay sessions, spend reservation, payment dispatch, completion attribution, and LDK reconciliation.
app/src/main/java/to/bitkit/repositories/QuickPaySpendStore.kt Implements the versioned daily ledger, local-day rollover, reservation persistence, settlement, and reconciliation.
app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt Integrates daily-cap eligibility and QuickPay completion outcomes into scan routing and pending-payment handling.
app/src/main/java/to/bitkit/repositories/PendingPaymentRepo.kt Adds keyed resolution caching so settlement occurring before pending-screen initialization can still be consumed.
app/src/main/java/to/bitkit/viewmodels/QuickPayViewModel.kt Reworks QuickPay around repository-owned sessions and maps success, pending, fallback, and error events to UI state.
app/src/main/java/to/bitkit/ui/settings/quickPay/QuickPaySettingsScreen.kt Adds the daily-limit multiplier slider and resolved daily-limit copy to QuickPay settings.
app/src/main/java/to/bitkit/ui/components/Slider.kt Updates slider layout and tap behavior for labeled QuickPay threshold and multiplier controls.
app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt Handles keyed QuickPay requests, fallback to Confirm, pending settlement amounts, and replacement navigation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Scan or paste Lightning request] --> B{QuickPay enabled and per-transaction threshold met?}
    B -->|No| C[Confirm payment]
    B -->|Yes| D{Daily ledger has capacity?}
    D -->|No| C
    D -->|Yes| E[Reserve spend by invoice hash]
    E --> F[Dispatch Lightning payment]
    F -->|Immediate success| G[Keep daily spend and show success with fee]
    F -->|Pending| H[Track pending payment]
    H -->|Later success| G
    F -->|Attributed failure| I[Release matching-day reservation]
    H -->|Later attributed failure| I
    I --> J[Show payment failure]
Loading

Reviews (5): Last reviewed commit: "fix: resolve quickpay review findings" | Re-trigger Greptile

@ovitrif ovitrif changed the title fix: require payment pin for quickpay fix: add quickpay daily spend limit Aug 15, 2026
Comment thread app/src/main/java/to/bitkit/ui/components/Slider.kt Fixed
@ovitrif
ovitrif force-pushed the fix/require-payment-pin-for-quickpay branch from 0807618 to 2e4a4c0 Compare August 15, 2026 18:28
@ovitrif
ovitrif force-pushed the cursor/fix-shop-quickpay-auth-86ae branch from 32b59a5 to 49ab192 Compare August 15, 2026 18:28
@ovitrif ovitrif self-assigned this Aug 15, 2026
Base automatically changed from cursor/fix-shop-quickpay-auth-86ae to master August 15, 2026 23:02
@ovitrif
ovitrif force-pushed the fix/require-payment-pin-for-quickpay branch from 2e4a4c0 to 7b9ea19 Compare August 15, 2026 23:03
ben-kaufman

This comment was marked as resolved.

Comment thread app/src/main/java/to/bitkit/repositories/QuickPayRepo.kt
Comment thread app/src/main/java/to/bitkit/repositories/QuickPayRepo.kt
Comment thread app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt Outdated
@ovitrif
ovitrif requested a review from ben-kaufman August 26, 2026 18:34
Comment thread app/src/main/java/to/bitkit/repositories/QuickPayRepo.kt Fixed
Comment thread app/src/main/java/to/bitkit/repositories/QuickPayRepo.kt
Comment thread app/src/main/java/to/bitkit/repositories/QuickPayRepo.kt Outdated
Comment thread app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt
Comment thread app/src/main/java/to/bitkit/repositories/QuickPayRepo.kt Outdated
@ovitrif
ovitrif requested a review from ben-kaufman August 26, 2026 20:39
@piotr-iohk

piotr-iohk commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Retested. Previous findings are fixed:

  • Dust invoices showing $0.00 no longer QuickPay once the daily cap is full (both platforms).
  • Wipe+restore: iOS used to reset today’s spend (fresh QuickPay allowance) while Android kept it. iOS now matches Android — multiplier and today’s spend both come back.

New issue, both platforms: paying an invoice via QuickPay, then scanning/pasting that same invoice again, shows Bitcoin Sent / success again. LDK rejects it as already paid (DuplicatePayment / "an invoice must not be paid twice"). There is no second payment and it does not add to the daily cap — the Success screen is a replay (and omits the routing fee).

Recordings + logs attached.

logs-android.zip

repeat.invoice.-.android.mov

@ovitrif
ovitrif marked this pull request as draft August 27, 2026 12:35
@ovitrif

This comment was marked as outdated.

Comment thread app/src/main/java/to/bitkit/ui/components/Slider.kt
@ovitrif

ovitrif commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

@piotr-iohk Fixed in 1f8525f. A fresh DuplicatePayment whose LDK row already succeeded now releases only the new reservation and shows Payment Failed with “This invoice has already been paid.” instead of replaying Bitcoin Sent. The original daily spend remains counted and no second charge is added. Recovery of a genuinely in-flight QuickPay still presents success. I added QA item 10 for the retest.

@ovitrif
ovitrif marked this pull request as ready for review August 27, 2026 13:54
@ovitrif
ovitrif requested a review from jvsena42 August 27, 2026 13:54

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retested, LGTM.

@piotr-iohk piotr-iohk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retested, LGTM.

@ovitrif
ovitrif dismissed jvsena42’s stale review August 27, 2026 14:27

addressed review findings

@ovitrif
ovitrif merged commit d5cd8a6 into master Aug 27, 2026
27 checks passed
@ovitrif
ovitrif deleted the fix/require-payment-pin-for-quickpay branch August 27, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: add quickpay daily spend limit

5 participants